package ch.epfl.chili.libgdx_sample; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import ch.epfl.chili.libgdx_sample.util.Size2D; import ch.epfl.chili.chilitags.Chilitags3D; import ch.epfl.chili.chilitags.ObjectTransform; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.PerspectiveCamera; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.Model; import com.badlogic.gdx.graphics.g3d.ModelBatch; import com.badlogic.gdx.graphics.g3d.ModelInstance; import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; import com.badlogic.gdx.graphics.g3d.utils.AnimationController; /** * The main application object * @author Ayberk Özgür */ public class LibgdxSample implements ApplicationListener { private Chilitags3D chilitags; //The Chilitags object private long lastMillis = 0; private double fps = 0; private final PlatformDependentMethods platformDeps; //The platform dependent global methods private final DeviceCameraController deviceCameraControl; //The webcam controller /* * Libgdx specific fields */ private Environment environment; //Our environment that contains the lights/camera/models etc. private PerspectiveCamera camera; //The camera that we use to view our 3D environment private AssetManager assets; //The manager that loads the 3D models/sprites etc. private Model treeModel; //Our tree model, we can create multiple ModelInstances from this private Map<String, ModelInstance> treeModelInstances; //The container that maps tag names to model instances private Map<String, AnimationController> treeModelAnimationControllers; //The container that maps tag names to animation controllers of model instances private ModelBatch modelBatch; //The object that we use to render all tree models in one frame private BitmapFont font; //The font that we use to draw the FPS messages private SpriteBatch spriteBatch; //The object that we use to render all fonts in one frame private boolean loading; //Indicates whether the model data is still loading in the beginning /** * Creates a new main libgdx-sample object * Every platform calls this method with their own platform-dependent instantiated objects * @param platformDependentMethods The platform-dependent method container * @param cameraControl The platform-dependent device camera controller */ public LibgdxSample(PlatformDependentMethods platformDependentMethods, DeviceCameraController cameraControl) { this.platformDeps = platformDependentMethods; this.deviceCameraControl = cameraControl; } @Override public void create() { /* * Libgdx objects */ //Create the maps for our model instances treeModelInstances = new HashMap<String, ModelInstance>(); treeModelAnimationControllers = new HashMap<String, AnimationController>(); modelBatch = new ModelBatch(); //Create asset manager and load our model assets = new AssetManager(); loading = true; assets.load("models/funky_palm_tree/funky_palm_tree.g3db", Model.class); //Create the environment and set the light in there environment = new Environment(); environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f)); environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, 0f, 0f, 100f)); //Create the objects needed to render text spriteBatch = new SpriteBatch(); font = new BitmapFont(); font.setColor(Color.RED); font.setScale(2); //Create the GL Camera camera = new PerspectiveCamera(67.0f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); camera.position.set(0.0f,0.0f,0.0f); //Camera position camera.up.set(0.0f,-1.0f,0.0f); //Up vector camera.lookAt(0.0f, 0.0f, 1.0f); //Forward vector camera.far = 10000.0f; //Far clipping plane distance camera.near = 0.1f; //Near clipping plane distance camera.update(); /* * Device camera controller */ deviceCameraControl.init(); /* * Chilitags object */ //Get the camera image size Size2D cameraSize = deviceCameraControl.getCameraSize(); //Get the image size that will be processed by Chilitags Size2D processingSize = deviceCameraControl.getProcessingSize(); //Create the actual Chilitags object chilitags = new Chilitags3D( cameraSize.width,cameraSize.height, processingSize.width,processingSize.height, Chilitags3D.InputType.values()[deviceCameraControl.getImageFormat().ordinal()]); double[] cc = { 270*processingSize.width/640, 0, processingSize.width/2, 0, 270*processingSize.width/640, processingSize.height/2, 0, 0, 1}; double[] dc = {}; chilitags.setCalibration(cc,dc); } /** * Creates the models when the assets are done loading */ private void doneLoading(){ treeModel = assets.get("models/funky_palm_tree/funky_palm_tree.g3db", Model.class); loading = false; } @Override public void dispose() { modelBatch.dispose(); spriteBatch.dispose(); deviceCameraControl.destroy(); } @Override public void render() { //Create model if assets are done loading if (loading && assets.update()) doneLoading(); //Calculate render loop FPS long currentMillis = System.currentTimeMillis(); try{ double fpsNow = 1000/(currentMillis - lastMillis); fps = 0.8*fps + 0.2*fpsNow; }catch(Exception e){} lastMillis = currentMillis; //Necessary GL calls Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); /* * Render the camera image first */ deviceCameraControl.renderBackground(); /* * Detect tags and draw trees on top of detected tags */ ObjectTransform[] results = chilitags.estimate(deviceCameraControl.getImage()); if(!loading){ //Announce that we are rendering 3D models now; this will render the 3D models on top of the camera image modelBatch.begin(camera); //Update all animations of instances for(Entry<String, AnimationController> instance : treeModelAnimationControllers.entrySet()) instance.getValue().update(Gdx.graphics.getDeltaTime()); //Traverse all detected tags for(int k=0;k<results.length;k++){ if(treeModelInstances.containsKey(results[k].name)){ //If detected tag already has a model instance associated with it //Update the model's transform with the new one from Chilitags ModelInstance tree = treeModelInstances.get(results[k].name); float[] mat = new float[16]; for(int i=0;i<4;i++) for(int j=0;j<4;j++) mat[j*4+i] = (float)results[k].transform[i][j]; tree.transform.set(mat); tree.transform.scale(10.0f, 10.0f, 10.0f); //Render current model instance modelBatch.render(tree, environment); } else{ //If detected tag does not yet have a model instance associated with it //Create new model instance ModelInstance newTree = new ModelInstance(treeModel); //Set transform with the one calculated by Chilitags float[] mat = new float[16]; for(int i=0;i<4;i++) for(int j=0;j<4;j++) mat[j*4+i] = (float)results[k].transform[i][j]; newTree.transform.set(mat); newTree.transform.scale(10.0f, 10.0f, 10.0f); //Add the new instance to the map treeModelInstances.put(results[k].name, newTree); //Create the animation controller for the new instance AnimationController newAnim = new AnimationController(newTree); //First, play the grow animation newAnim.setAnimation("grow"); //Then, play the sway animation in a loop forever newAnim.queue("sway", -1, 1.0f, null, 0.5f); //Add the animation controller to the map treeModelAnimationControllers.put(results[k].name, newAnim); } } //Announce that we are done rendering 3D models modelBatch.end(); } /* * Render FPS texts */ //Announce that we are rendering sprites now; this will render the text above the camera image and 3D models spriteBatch.begin(); //Draw FPS texts font.draw(spriteBatch,"RENDER: " + (int)fps+" FPS",30,30); font.draw(spriteBatch,"CAM: " + (int)deviceCameraControl.getFPS() +" FPS",30,60); //Announce that we are done rendering sprites spriteBatch.end(); } @Override public void resize(int width, int height) { //Recreate the GL camera camera = new PerspectiveCamera(67.0f, 2.0f * width / height, 2.0f); camera.position.set(0.0f,0.0f,0.0f); camera.up.set(0.0f,-1.0f,0.0f); camera.lookAt(0.0f, 0.0f, 1.0f); camera.far = 10000.0f; camera.near = 0.1f; camera.update(); } @Override public void pause() { } @Override public void resume() { } }